import random
from collections import defaultdict
from mahjong_tracker import MahjongTracker  # 导入记牌器
from winMaj import is_winning_hand


class MahjongGame:
    def __init__(self, players: list):
        """初始化麻将游戏"""
        self.players = players  # 玩家列表
        self.player_hands = {player: [] for player in players}  # 玩家手牌
        self.discards = defaultdict(list)  # 玩家弃牌记录
        self.wall = self._create_tile_wall()  # 牌墙
        self.current_player_index = 0  # 当前玩家索引
        self.dealer = players[0] if players else ""  # 庄家
        self.tracker = MahjongTracker(self.dealer)  # 初始化记牌器
        # 添加碰牌和杠牌存储
        self.pong_tiles = defaultdict(list)  # 存储玩家的碰牌
        self.gang_tiles = defaultdict(list)  # 存储玩家的杠牌

    def _check_win(self, player):
        """检查玩家是否胡牌"""
        if is_winning_hand(self.player_hands[player]):
            print(f"\n{'=' * 30}")
            self.print_game_status()
            print(f"🎉 {player} 胡牌！游戏结束")
            print(f"{player}的手牌:", " ".join(sorted(self.player_hands[player])))
            print(f"{player}的碰牌:", (self.pong_tiles[player]))
            print(f"{player}的杠牌:", (self.gang_tiles[player]))
            print(f"{player}的弃牌:", (self.discards[player]))

            print(f"{'=' * 30}\n")
            return True
        return False
    def _choose_tile(self, player):
        # 选择出牌，对玩家手牌进行优先级评估
        # 按照优先级排序：1. 单牌字牌 2.其他单牌 3. 对子 4. 刻子 5. 顺子
        sorted_hands = sorted(self.player_hands[player], key=self._tile_sort_key)
        # 选择优先级最高的牌
        chosen_tile = sorted_hands[0] if sorted_hands else None
        if chosen_tile:
            print(f"{player}选择出牌: {chosen_tile}")
        else:
            print(f"{player}没有可出的牌")




    def _create_tile_wall(self):
        """创建麻将牌墙"""
        wall = []

        # 添加万、条、饼花色（1-9）
        for suit in ['万', '条', '饼']:
            for num in range(1, 10):
                # 每种牌4张
                for _ in range(4):
                    wall.append(f"{num}{suit}")

        # 添加字牌（东南西北中发白）
        for wind in ['东', '南', '西', '北']:
            for _ in range(4):
                wall.append(wind)
        for dragon in ['中', '发', '白']:
            for _ in range(4):
                wall.append(dragon)

        # 洗牌
        random.shuffle(wall)
        return wall

    def deal_initial_tiles(self):
        """初始发牌"""
        # 庄家14张，其他玩家13张
        for i, player in enumerate(self.players):
            tiles_count = 14 if i == 0 else 13
            self.player_hands[player] = [self.wall.pop() for _ in range(tiles_count)]
            # 对手牌排序
            self.player_hands[player].sort(key=self._tile_sort_key)
            # 初始化后调用一次记牌器状态
            # if player == self.dealer:
            #     self.tracker.track_initial_hand(player, self.player_hands[player])

    def _tile_sort_key(self, tile):
        """手牌排序的辅助函数"""
        # 定义排序优先级
        suit_order = {'万': 0, '条': 1, '饼': 2, '东': 3, '南': 4, '西': 5, '北': 6, '中': 7, '发': 8, '白': 9}

        # 获取当前正在排序的玩家（通过调用栈或传参方式）
        # 这里需要修改逻辑来正确识别是哪个玩家的手牌在排序

        # 统计当前牌的数量特征
        current_count = self.player_hands[self.players[self.current_player_index]].count(tile)

        # 组合类型优先级：四张(0) > 三张(1) > 对子(2) > 单张(3)
        if current_count >= 4:
            group_priority = 0
        elif current_count == 3:
            group_priority = 1
        elif current_count == 2:
            group_priority = 2
        else:  # 单张
            group_priority = 3

        # 数字牌排序
        if tile[0].isdigit():
            num = int(tile[0])
            suit = tile[1:]
            return (group_priority, suit_order[suit], num)

        # 字牌排序
        return (group_priority, suit_order[tile], 0)

    def next_player(self):
        """切换到下一位玩家"""
        self.current_player_index = (self.current_player_index + 1) % len(self.players)

    def draw_tile(self, player):
        """玩家摸牌"""
        if self.wall:
            tile = self.wall.pop()
            self.player_hands[player].append(tile)
            # 保持手牌排序
            self.player_hands[player].sort(key=self._tile_sort_key)
            return tile
        return None

    def discard_tile(self, player, tile_index):
        #
        """玩家弃牌"""
        discarded_tile = self.player_hands[player].pop(tile_index)
        self.discards[player].append(discarded_tile)
        # 调用记牌器API（不包括自己）
        # if player != self.dealer:
        #     self.tracker.track_discard(player, discarded_tile)
        return discarded_tile

        # 新增：检查玩家是否可以碰指定牌
    def can_pong(self, player, tile):
        """检查玩家是否可以碰指定牌（手中有2张相同牌）"""
        return self.player_hands[player].count(tile) == 2

    # 新增：检查玩家是否可以杠指定牌
    def can_gang(self, player, tile):
        """检查玩家是否可以杠指定牌（手中有3张相同牌）"""
        return self.player_hands[player].count(tile) == 3

    def check_self_gang(self, player):
        """检查玩家是否可以自杠（暗杠或冲杠）"""
        # 暗杠检查：手牌中存在四张相同的牌
        hand_tiles = self.player_hands[player]
        tile_counts = {}
        for tile in hand_tiles:
            tile_counts[tile] = tile_counts.get(tile, 0) + 1
            if tile_counts[tile] == 4:
                print(f"{player}暗杠: {tile}")
                self.process_self_gang(player, tile)
                return True

        # 冲杠检查：碰牌中存在三张且手牌有第四张
        for pong_set in self.pong_tiles[player]:
            pong_tile = pong_set[0]
            if hand_tiles.count(pong_tile) >= 1:
                print(f"{player}冲杠: {pong_tile}")
                self.process_self_gang(player, pong_tile, is_bugang=True)
                return True
        return False

    def process_self_gang(self, player, tile, is_bugang=False):
        """处理自摸杠（暗杠/冲杠）"""
        if is_bugang:
            # 冲杠：从碰牌区移除三张，加入杠牌区
            for pong_set in self.pong_tiles[player]:
                if pong_set[0] == tile and len(pong_set) == 3:
                    self.pong_tiles[player].remove(pong_set)
                    self.gang_tiles[player].append(pong_set + [tile])
                    # 从手牌移除最后一张
                    self.player_hands[player].remove(tile)
                    break
        else:
            # 暗杠：从手牌移除四张
            for _ in range(4):
                self.player_hands[player].remove(tile)
            self.gang_tiles[player].append([tile] * 4)

        # 摸岭上牌
        if self.wall:
            gang_tile = self.wall.pop()
            self.player_hands[player].append(gang_tile)
            self.player_hands[player].sort(key=self._tile_sort_key)
            print(f"{player}摸岭上牌: {gang_tile}")

        if self._check_win(player):
            self.game_over = True
            return

        # 打出一张牌
        if self.player_hands[player]:
            tile_to_discard = -1
            discarded = self.discard_tile(player, tile_to_discard)
            print(f"{player}打牌: {discarded}")

        # 更新当前玩家
        self.current_player_index = (self.players.index(player) + 1) % len(self.players)
        self.print_game_status()

    def can_gang_self(self, player):
        """检查玩家所有手牌是否可以杠指定牌（手中有4张相同牌）"""
        for tile in self.player_hands[player]:
            if self.player_hands[player].count(tile) == 4:
                print(f"{player}自己杠牌: {tile}")
                process_gang = self.process_gang(player, tile, player)
                return True
        return False

    # 新增：处理玩家碰牌操作
    def process_pong(self, player, discarded_tile, discarder):
        """处理玩家碰牌：添加弃牌到碰牌区，排序后弃一张牌"""
        print(f"{player}碰了{discarder}的{discarded_tile}")

        for _ in range(2):
            self.player_hands[player].remove(discarded_tile)  # 这里可能需要处理牌的张数表示，具体取决于你的手牌数据结构

        # 2. 将新形成的刻子（三张相同的牌）添加到玩家的碰牌集合中
        # 通常，一个刻子用一个包含三张相同牌的列表表示
        new_pong_set = [discarded_tile, discarded_tile, discarded_tile]  # 这里代表三张相同的牌
        self.pong_tiles[player].append(new_pong_set)  # 注意，这里只 append 一次，添加的是一个代表刻子的列表

        self.player_hands[player].sort(key=self._tile_sort_key)

        # 简单AI：丢弃最后一张牌
        tile_to_discard = -1
        discarded = self.discard_tile(player, tile_to_discard)
        print(f"{player}打牌: {discarded}")

        # 更新当前玩家为碰牌玩家的下一位
        self.current_player_index = (self.players.index(player) + 1) % len(self.players)
        self.print_game_status()

    # 新增：处理玩家杠牌操作
    def process_gang(self, player, discarded_tile, discarder):
        """处理玩家杠牌：添加弃牌到手牌，摸岭上牌后弃一张牌"""
        self.player_hands[player].append(discarded_tile)
        self.player_hands[player].sort(key=self._tile_sort_key)
        print(f"{player}杠了{discarder}的{discarded_tile}")

        for _ in range(3):
            self.player_hands[player].remove(discarded_tile)  # 这里可能需要处理牌的张数表示，具体取决于你的手牌数据结构

            # 2. 将新形成的刻子（三张相同的牌）添加到玩家的碰牌集合中
            # 通常，一个刻子用一个包含三张相同牌的列表表示
        new_pong_set = [discarded_tile, discarded_tile, discarded_tile,discarded_tile]  # 这里代表三张相同的牌
        self.gang_tiles[player].append(new_pong_set)  # 注意，这里只 append 一次，添加的是一个代表刻子的列表

        # 摸岭上牌（从牌墙末尾取牌）
        if self.wall:
            gang_tile = self.wall.pop()
            self.player_hands[player].append(gang_tile)
            self.player_hands[player].sort(key=self._tile_sort_key)
            print(f"{player}摸岭上牌: {gang_tile}")
        else:
            print("牌墙已空，无法摸岭上牌")

        if self._check_win(player):
            self.game_over = True
            return

        self.player_hands[player].sort(key=self._tile_sort_key)


        # 简单AI：丢弃最后一张牌
        if self.player_hands[player]:
            tile_to_discard = -1
            discarded = self.discard_tile(player, tile_to_discard)
            print(f"{player}打牌: {discarded}")

        # 更新当前玩家为杠牌玩家的下一位
        self.current_player_index = (self.players.index(player) + 1) % len(self.players)
        self.print_game_status()

    # 新增：检查所有玩家是否可以碰/杠当前弃牌
    def check_pong_gang(self, discarded_tile, discarder):
        """按顺序检查其他玩家是否可以碰/杠，返回是否有人执行了操作"""
        num_players = len(self.players)
        if num_players <= 1:
            return False

        current_index = self.current_player_index
        # 按逆时针顺序检查其他玩家（从下一位开始）
        for i in range(1, num_players):
            player_index = (current_index + i) % num_players
            player = self.players[player_index]
            if player == discarder:
                continue  # 不能碰/杠自己的弃牌

            # 先检查杠（优先级高于碰）
            if self.can_gang(player, discarded_tile):
                self.process_gang(player, discarded_tile, discarder)
                return True

            # 再检查碰
            if self.can_pong(player, discarded_tile):
                self.process_pong(player, discarded_tile, discarder)
                return True

        return False  # 无人碰或杠

    def print_game_status(self):
        """打印当前游戏状态"""
        print("\n" + "=" * 50)
        print(f"当前玩家: {self.players[self.current_player_index]}")
        print(f"剩余牌数: {len(self.wall)}")

        # 打印各玩家手牌
        for player in self.players:
            status = " <-- 当前玩家" if player == self.players[self.current_player_index] else ""
            print(f"\n{player}的手牌{status}:")
            print(" ".join(sorted(self.player_hands[player], key=self._tile_sort_key)))

            # 打印碰牌 杠牌
            if self.gang_tiles[player] or self.pong_tiles[player]:
                if self.pong_tiles[player]:
                    print(f"{player}的碰牌:")
                    for pong_set in self.pong_tiles[player]:
                        print(" ".join(pong_set))

                if self.gang_tiles[player]:
                    print(f"{player}的杠牌:")
                    for pong_set in self.gang_tiles[player]:
                        print(" ".join(pong_set))

            # 打印弃牌
            if self.discards[player]:
                print(f"{player}的弃牌:")
                print(" ".join(self.discards[player]))




        print("=" * 50 + "\n")

    def play_round(self):
        """玩一局游戏"""
        print("===== 开始新一局麻将 =====")
        self.deal_initial_tiles()
        self.print_game_status()

        # 庄家先打牌（不摸牌）
        current_player = self.players[self.current_player_index]
        print(f"{current_player}开局打牌")



        # 新增：开局时检查自杠/胡牌
        if not self.check_self_gang(current_player) and not self._check_win(current_player):
            # 原有打牌逻辑
            if len(self.player_hands[current_player]) > 0:
                tile_to_discard = -1
                discarded_tile = self.discard_tile(current_player, tile_to_discard)
                if discarded_tile:
                    print(f"{current_player}打牌: {discarded_tile}")
                    if not self.check_pong_gang(discarded_tile, current_player):
                        self.next_player()
                else:
                    print(f"{current_player}无法打牌，手牌为空")

        # 模拟游戏过程，打到没牌为止
        while True:
            current_player = self.players[self.current_player_index]

            # 摸牌
            drawn_tile = self.draw_tile(current_player)
            if not drawn_tile:
                print("牌墙已空，游戏结束")
                break
            print(f"{current_player}摸牌: {drawn_tile}")

            if self._check_win(current_player):
                self.game_over = True
                break

            # 新增自杠检查
            if self.check_self_gang(current_player):
                continue  # 如果执行了杠操作，跳过后续打牌步骤

            # 确保有牌可打
            if len(self.player_hands[current_player]) == 0:
                print(f"{current_player}手牌为空，无法打牌")
                self.next_player()
                continue

            # 简单AI：丢弃最后一张牌（实际游戏中应该根据策略选择）
            # 智能选择出牌

            tile_to_discard = -1  # 最后一张牌
            discarded_tile = self.discard_tile(current_player, tile_to_discard)

            # 确保弃牌成功
            if discarded_tile:
                print(f"{current_player}打牌: {discarded_tile}")
                # 显示游戏状态
                self.print_game_status()
                self.tracker.print_tracker_status()
                # 新增：检查是否有玩家可以碰或杠
                if not self.check_pong_gang(discarded_tile, current_player):
                    # 无人碰杠，切换到下一位玩家
                    self.next_player()
            else:
                print(f"{current_player}弃牌失败")
                self.next_player()

            # 显示游戏状态
            self.print_game_status()
            # self.tracker.print_tracker_status()


# 运行游戏
if __name__ == "__main__":
    # 创建4位玩家
    players = ["玩家1（庄）", "玩家2", "玩家3", "玩家4"]

    # 初始化并开始游戏
    game = MahjongGame(players)
    game.play_round()
